package com.sufen.seckill.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sufen.seckill.exception.GlobalException;
import com.sufen.seckill.pojo.Order;
import com.sufen.seckill.pojo.SeckillMessage;
import com.sufen.seckill.pojo.SeckillOrder;
import com.sufen.seckill.pojo.User;
import com.sufen.seckill.rabbitmq.MQSender;
import com.sufen.seckill.service.IGoodsService;
import com.sufen.seckill.service.IOrderService;
import com.sufen.seckill.service.ISeckillOrderService;
import com.sufen.seckill.utils.JsonUtil;
import com.sufen.seckill.vo.GoodsVo;
import com.sufen.seckill.vo.RespBean;
import com.sufen.seckill.vo.RespBeanEnum;
import com.wf.captcha.ArithmeticCaptcha;
import com.wf.captcha.base.Captcha;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Author sufen
 * @Date 2021/10/21 12:03
 * @Version 1.0
 * 功能描述：点秒杀按钮后负责处理的Controller：1.校验2.生成订单（Mapper）
 */
@Controller
@RequestMapping("/seckill")
@Slf4j
public class SecKillController implements InitializingBean {
    @Autowired
    private IGoodsService goodsService;
    @Autowired
    private ISeckillOrderService seckillOrderService;
    @Autowired
    private IOrderService orderService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MQSender mqSender;

    @Autowired
    private RedisScript<Long> script;

    private Map<Long,Boolean> EmptyStockMap=new HashMap<>();

    @RequestMapping("/doSeckill2")
    public String doSecKill2(User user, Long goodsId, Model model){
        if(user==null){
            return "login";
        }
        GoodsVo goods =goodsService.findGoodsVoByGoodsId(goodsId);
        //判断库存//这里的stockCount取得是秒杀表里的库存。
        // 虽然是goodsService，但是它对应的mapper里面goods表和goods_seckill表连接了，
        // 这个stockCount是连接后秒杀表的库存字段。
        if(goods.getStockCount()<1){
            model.addAttribute("errmsg", RespBeanEnum.EMPTY_STOCK.getMessage());
            return "secKillFail";
        }
        //判断重复抢购，用户ID和商品ID
        SeckillOrder seckillOrder=seckillOrderService.getOne(new QueryWrapper<SeckillOrder>().eq("user_id",user.getId()).eq("goods_id",goodsId));
        if(seckillOrder!=null){
            model.addAttribute("errmsg",RespBeanEnum.REPEATE_ERROR.getMessage());
            return "secKillFail";
        }
        Order order=orderService.seckill(user, goods);
        model.addAttribute("order",order);
        model.addAttribute("goods",goods);
        return "orderDetail";
    }

    /**
     * 10月24日，40，秒杀静态化，为的是动静分离，不能用model传thymeleaf这种页面了。
     * 要传静态htm给浏览器缓存，动态的就用ajax去请求。
     * 实现步骤：在SecKillController里面复制秒杀接口对应的处理地址的controller代码，
     * 然后把model给前端传参的语句全注释了，然后把model传参的所有参数写一个Vo传给前端,
     * 用RespBean.success(obj)传,这样的话,前端能拿到这个obj,也就是order对象,
     * order对象里有所有的数据库的字段的数据,这样前端拿到之后,就能通过他来展示一些信息了.
     * 还能用RespBean的success或error来取出code,如果是200就返回正确结果的页面+obj的数据信息,
     * 如果RespBean的success或error不是200还可以取出code对应的message,通过前端的layer展示出来.
     * (用于给ajax返回请求的结果实体对象，其实这个DetailVo里面的参数和之前model传给thymeleaf的是一摸一样的。
     * @param user
     * @param goodsId
     * @return
     */
    @RequestMapping(value = "/{path}/doSeckill",method = RequestMethod.POST)
    @ResponseBody
    public RespBean doSecKill(@PathVariable String path, User user, Long goodsId){
//        业务逻辑：1.判断重复抢购
//            2.判断库存
//            3.执行秒杀逻辑：1.减库存2.插入订单表3.插入秒杀订单表
        if(user==null){
            return RespBean.error(RespBeanEnum.User_NULL_ERROR);
        }

        //10月28日，用redis减库存，能减则把后续请求发送给rabbitmq异步处理，不能减就算了
        ValueOperations valueOperations=redisTemplate.opsForValue();

        Boolean check=orderService.checkPath(user,goodsId,path);
        if(!check){
            return RespBean.error(RespBeanEnum.REQUEST_ILLEGAL);
        }

        //判断重复抢购，用户ID和商品ID
        SeckillOrder seckillOrder=(SeckillOrder) redisTemplate.opsForValue().get("order:"+user.getId()+":"+goodsId);
        if(seckillOrder!=null){
            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
        }
        //内存标记，减少redis访问
        if(EmptyStockMap.get(goodsId)){
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }
        //减redis里的库存
//        Long stock=valueOperations.decrement("seckillGoods:"+goodsId);
        Long stock=(Long) redisTemplate.execute(script, Collections.singletonList("seckillGoods:"+goodsId),Collections.EMPTY_LIST);
        if(stock<0){
            EmptyStockMap.put(goodsId,true);
            valueOperations.increment("seckillGoods:"+goodsId);
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }
        SeckillMessage seckillMessage = new SeckillMessage(user, goodsId);
        mqSender.sendSeckillMessage(JsonUtil.object2JsonStr(seckillMessage));
        return RespBean.success(0);



        //        GoodsVo goods =goodsService.findGoodsVoByGoodsId(goodsId);
//        //1.判断库存//这里的stockCount取得是秒杀表里的库存。
//        // 虽然是goodsService，但是它对应的mapper里面goods表和goods_seckill表连接了，
//        // 这个stockCount是连接后秒杀表的库存字段。
//        if(goods.getStockCount()<1){
////            model.addAttribute("errmsg", RespBeanEnum.EMPTY_STOCK.getMessage());
////            return "secKillFail";
//            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
//        }
//        //2.判断重复抢购，用户ID和商品ID
////        SeckillOrder seckillOrder=seckillOrderService.getOne(new QueryWrapper<SeckillOrder>().eq("user_id",user.getId()).eq("goods_id",goodsId));
//        SeckillOrder seckillOrder=(SeckillOrder) redisTemplate.opsForValue().get("order:"+user.getId()+":"+goodsId);
//        if(seckillOrder!=null){
////            model.addAttribute("errmsg",RespBeanEnum.REPEATE_ERROR.getMessage());
////            return "secKillFail";
//            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
//        }
//        //3.执行秒杀逻辑
//        Order order=orderService.seckill(user, goods);
////        model.addAttribute("order",order);
////        model.addAttribute("goods",goods);
////        return "orderDetail";
//        return RespBean.success(order);

    }

    //获取秒杀结果

    /**
     *
     * @param user
     * @param goodsId
     * @return orderId:成功；-1：秒杀失败；0：排队中
     */
    @RequestMapping(value = "/getResult",method = RequestMethod.GET)
    @ResponseBody
    public RespBean getResult(User user,Long goodsId){
        if(user==null){
            return RespBean.error(RespBeanEnum.User_NULL_ERROR);
        }
        Long orderId=seckillOrderService.getResult(user,goodsId);
        return RespBean.success(orderId);
    }

    @RequestMapping(value="/path",method = RequestMethod.GET)
    @ResponseBody
    public RespBean getPath(User user,Long goodsId,String captcha){
        if(user==null){
            return RespBean.error(RespBeanEnum.User_NULL_ERROR);
        }

        Boolean check=orderService.checkCaptcha(user,goodsId,captcha);
        if(!check){
            return RespBean.error(RespBeanEnum.ERROR_CAPTCHA);
        }
        String str=orderService.createPath(user,goodsId);
        return RespBean.success(str);
    }

//    @RequestMapping(value = "/captcha",method = RequestMethod.GET)
//    public void verifyCode(User user, Long goodsId, HttpServletResponse response){
//        if(user==null||goodsId<0){
//            throw new GlobalException(RespBeanEnum.REQUEST_ILLEGAL);
//        }
//        //设置请求头
//        response.setContentType("image/jpg");
//        response.setHeader("Pargam","No-cache");
//        response.setHeader("Cache-Control","no-cache");
//        response.setDateHeader("Expires",0);
//        //生成验证码，将结果放入redis
//        ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 32, 3);
//        redisTemplate.opsForValue().set("captcha:"+user.getId()+":"+goodsId, captcha.text(),300, TimeUnit.SECONDS);
//        try {
//            captcha.out(response.getOutputStream());
//        } catch (IOException e) {
//            log.error("验证码生成失败",e.getMessage());
//        }
//    }

    @RequestMapping(value = "/captcha", method = RequestMethod.GET)
    public void verifyCode(User user, Long goodsId, HttpServletResponse response) {
        if (null==user||goodsId<0){
            throw new GlobalException(RespBeanEnum.REQUEST_ILLEGAL);
        }
        // 设置请求头为输出图片类型
        response.setContentType("image/jpg");
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        //生成验证码，将结果放入redis
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 32, 3);
        redisTemplate.opsForValue().set("captcha:"+user.getId()+":"+goodsId,captcha.text(),300, TimeUnit.SECONDS);
        try {
            captcha.out(response.getOutputStream());
        } catch (IOException e) {
            log.error("验证码生成失败",e.getMessage());
        }
    }

    /**
     * 系统初始化，把商品数量加载到redis里面去
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        List<GoodsVo> list=goodsService.findGoodsVo();
        if(CollectionUtils.isEmpty(list)){
            return;
        }
        for( GoodsVo goodsVo:list){
            redisTemplate.opsForValue().set("seckillGoods:"+goodsVo.getId(),goodsVo.getStockCount());
            EmptyStockMap.put(goodsVo.getId(),false);
        }
    }
}
